////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2005.
// -------------------------------------------------------------------------
//  File name:   FacialInstance.cpp
//  Version:     v1.00
//  Created:     18/10/2005 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "FacialInstance.h"
#include "FaceAnimation.h"
#include "FacialModel.h"
#include "FaceAnimSequence.h"
#include "LipSync.h"
#include "FaceEffectorLibrary.h"
#include "../PathExpansion.h"

#include <I3DEngine.h>
#include <CryHeaders.h>
#include <ISound.h>
#include "../CharacterManager.h"	
#include "../CharacterInstance.h"

#define RANDOM_IDLES_EFFECTOR "Random_Idles"
#define HEAD_PITCH_EFFECTOR  "Head_Pitch"
#define HEAD_YAW_EFFECTOR    "Head_Yaw"
#define HEAD_ROLL_EFFECTOR   "Head_Roll"

#define EYES_LEFT_RIGHT_EFFECTOR "Eyes_LeftRight"
#define EYES_UP_DOWN_EFFECTOR    "Eyes_UpDown"

#define BLINK_EFFECTOR "Blink"
#define BLINK_PAUSE (4.0f)
#define BLINK_DURATION (0.1f)

//////////////////////////////////////////////////////////////////////////
CFacialInstance::CFacialInstance( CFacialModel* pModel,CSkinInstance* pChar,CCharInstance* pMaster )
{
	m_pModel = pModel;
	m_pFaceState = pModel->CreateState();
	
	m_pCharacter = pChar;
	m_pMasterInstance = pMaster;
	
	m_pAnimContext = new CFacialAnimationContext(this);

	//m_procFace.fCurTime = 0;

	m_pEyeMovement = new CEyeMovementFaceAnim(this);

	m_pMasterInstance->m_SkeletonAnim.m_facialDisplaceInfo.Initialize(m_pMasterInstance->m_pModel->GetNumJoints());

	// procedural eyes movements
	m_tProcFace.m_JitterEyesPositions[0].Set(-1,-1,-4);
	m_tProcFace.m_JitterEyesPositions[1].Set(1,1,-2);
	m_tProcFace.m_JitterEyesPositions[2].Set(1,1,-4);
	m_tProcFace.m_JitterEyesPositions[3].Set(-1,-1,-2);
	m_tProcFace.m_nCurrEyesJitter=0;
	m_tProcFace.m_fEyesJitterScale=0.008f; 
	m_tProcFace.m_fLastJitterTime=0;
	m_tProcFace.m_fJitterTimeChange=2.0;
	m_tProcFace.m_pBlink = (CFacialEffector*)FindEffector("Blink" );
	m_tProcFace.m_bEyesBlinking=false;
	m_tProcFace.m_fBlinkingTime=-1;
	m_tProcFace.m_bEnabled=true;

	m_currentLayer = -1;
	Reset();
}

//////////////////////////////////////////////////////////////////////////
IFacialModel* CFacialInstance::GetFacialModel()
{
	return m_pModel;	
}

//////////////////////////////////////////////////////////////////////////
IFaceState* CFacialInstance::GetFaceState()
{
	return m_pFaceState;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::Update( CFacialDisplaceInfo& info, float fDeltaTimeSec, const QuatTS& rAnimLocationNext )
{
	if( Console::GetInst().ca_UseFacialAnimation==0 )
		return;

	m_pCharacter->m_Morphing.UpdateMorphEffectors(fDeltaTimeSec, rAnimLocationNext);

#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
	if (m_frameSkipper.UpdateDue())
#endif
	{
		UpdatePlayingSequences(fDeltaTimeSec, rAnimLocationNext);
		UpdateProceduralFaceBehaviour();
		CFacialEffectorsLibrary* pLibrary = (m_pModel ? static_cast<CFacialEffectorsLibrary*>(m_pModel->GetLibrary()) : 0);
		m_pEyeMovement->Update(fDeltaTimeSec, rAnimLocationNext, m_pCharacter, pLibrary, m_pFaceState);

		if (m_pAnimContext->Update( *m_pFaceState, rAnimLocationNext ))
		{
			CryAutoLock<CryCriticalSectionNonRecursive> lock(m_pCharacter->m_Morphing.m_lock);
			
			m_pCharacter->m_Morphing.ClearFinishedMorphEffectors();
			m_pModel->FillInfoMapFromFaceState(info, m_pFaceState,m_pCharacter->m_Morphing.m_arrMorphEffectors, &m_pMasterInstance->m_SkeletonPose,m_forcedRotations.size(),m_forcedRotations.begin(),m_pAnimContext->GetBoneRotationSmoothRatio());
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::UpdatePlayingSequences( float fDeltaTimeSec, const QuatTS& rAnimLocationNext )
{
	m_pAnimContext->UpdatePlayingSequences(rAnimLocationNext);

	// Check whether the sequence has finished.
	IFacialAnimSequence* pSequence = (m_currentLayer >= 0 ? m_layers[m_currentLayer].sequence : _smart_ptr<IFacialAnimSequence>(0));
	if (pSequence && !m_pAnimContext->IsPlayingSequence((CFacialAnimSequence*)pSequence))
	{
		if (Console::GetInst().ca_DebugFacial)
			CryLogAlways("CFacialInstance::Update - (this=%p) sequence \"%s\" finished", this, (pSequence ? pSequence->GetName() : "NULL"));
		IFacialAnimSequence* pOldSequence = (m_currentLayer >= 0 ? m_layers[m_currentLayer].sequence : _smart_ptr<IFacialAnimSequence>(0));
		if (m_currentLayer >= 0)
			m_layers[m_currentLayer] = LayerInfo();
		UpdateCurrentSequence(pOldSequence);
	}

	ProcessWaitingLipSync();
}

//////////////////////////////////////////////////////////////////////////
uint32 CFacialInstance::StartEffectorChannel(IFacialEffector* pEffector,float fWeight,float fFadeTime,float fLifeTime,int nRepeatCount)
{
	uint32 uChannelID = ~0;
	if (m_pAnimContext)
		uChannelID = m_pAnimContext->FadeInChannel(pEffector, fWeight, fFadeTime, fLifeTime, nRepeatCount);
	return uChannelID;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::StopEffectorChannel(uint32 nChannelID, float fFadeOutTime)
{
	if (m_pAnimContext)
		m_pAnimContext->StopChannel(nChannelID, fFadeOutTime);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::PreviewEffector( IFacialEffector *pEffector,float fWeight,float fBalance )
{
	m_pAnimContext->RemoveAllPreviewChannels();

	if (pEffector)
	{
		SFacialEffectorChannel channel;
		channel.bPreview = true;
		channel.status = SFacialEffectorChannel::STATUS_ONE;
		channel.pEffector = (CFacialEffector*)pEffector;
		channel.fWeight = fWeight;
		channel.fBalance = fBalance;

		m_pAnimContext->StartChannel( channel  );
	}
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::PreviewEffectors( IFacialEffector **pEffectors,float *fWeights,float *fBalances,int nEffectorsCount )
{
	m_pAnimContext->RemoveAllPreviewChannels();

	for (int i = 0; i < nEffectorsCount; i++)
	{
		if (fWeights[i] == 0)
			continue;

		SFacialEffectorChannel channel;
		channel.bPreview = true;
		channel.status = SFacialEffectorChannel::STATUS_ONE;
		channel.pEffector = (CFacialEffector*)pEffectors[i];
		channel.fWeight = fWeights[i];
		channel.fBalance = fBalances[i];

		m_pAnimContext->StartChannel( channel  );

	}
}

//////////////////////////////////////////////////////////////////////////
IFacialEffector* CFacialInstance::FindEffector( CFaceIdentifierHandle ident )
{
	IFacialEffectorsLibrary *pLib = m_pModel->GetLibrary();
	if (pLib)
	{
		return pLib->Find(ident);
	}
	return NULL;
}

IFacialEffector* CFacialInstance::FindEffector( const char* identStr )
{
	IFacialEffectorsLibrary *pLib = m_pModel->GetLibrary();
	if (pLib)
	{
		return pLib->Find(identStr);
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::PlaySequence(IFacialAnimSequence* pSequence, EFacialSequenceLayer layer, bool bExclusive, bool bLooping)
{
	if (Console::GetInst().ca_DebugFacial)
	{
		CryLogAlways("CFacialInstance::PlaySequence(this=%p, sequence=\"%s\", layer=%d)",
			this, (pSequence ? pSequence->GetName() : "NULL"), layer);
	}

	IFacialAnimSequence* pOldSequence = (m_currentLayer >= 0 ? m_layers[m_currentLayer].sequence : _smart_ptr<IFacialAnimSequence>(0));

	m_layers[layer] = LayerInfo(pSequence, bExclusive, bLooping);

	// TODO: Better way to handle this? (left over when moving from CAnimatedCharacter to here).
	m_layers[layer].bLooping = m_layers[layer].bLooping ||
		(layer == eFacialSequenceLayer_AIExpression) || (layer == eFacialSequenceLayer_AGStateAndAIAlertness);

	UpdateCurrentSequence(pOldSequence);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::SeekSequence(EFacialSequenceLayer layer, float fTime)
{
	CFacialAnimSequence* pSequence = (CFacialAnimSequence*)m_layers[layer].sequence.get();
	if (pSequence)
		m_pAnimContext->SeekSequence(pSequence, fTime);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::StopSequence(EFacialSequenceLayer layer)
{
	if (Console::GetInst().ca_DebugFacial)
	{
		CryLogAlways("CFacialInstance::StopSequence(this=%p, sequence=\"%s\", layer=%d)",
			this, (m_layers[layer].sequence ? m_layers[layer].sequence->GetName() : "NULL"), layer);
	}

	IFacialAnimSequence* pOldSequence = (m_currentLayer >= 0 ? m_layers[m_currentLayer].sequence : _smart_ptr<IFacialAnimSequence>(0));

	CFacialAnimSequence* pSequence = (CFacialAnimSequence*)m_layers[layer].sequence.get();
	if (pSequence)
		m_pAnimContext->StopSequence(pSequence);
	m_layers[layer] = LayerInfo();
	UpdateCurrentSequence(pOldSequence);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::StopAllSequences()
{
	m_pAnimContext->StopAllSequences();
	Reset();
}

//////////////////////////////////////////////////////////////////////////
bool CFacialInstance::IsPlaySequence( IFacialAnimSequence *pSequence, EFacialSequenceLayer layer )
{
	if (layer < 0 || layer >= eFacialSequenceLayer_COUNT)
	{
		CryWarning(VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING,"Invalid layer passed - %d", layer);
		return false;
	}

	return m_layers[layer].sequence == pSequence;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::PauseSequence( EFacialSequenceLayer layer, bool bPaused )
{
	CFacialAnimSequence* pSequence = (CFacialAnimSequence*)m_layers[layer].sequence.get();
	if (pSequence)
		m_pAnimContext->PauseSequence(pSequence, bPaused);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::LipSyncWithSound( uint32 nSoundId, bool bStop )
{
	ProcessWaitingLipSync();

	if (!m_waitingLipSync.IsWaiting())
	{

		ISound *pSound = gEnv->pSoundSystem->GetSound(nSoundId);
		if (!pSound)
			return;
		stack_string sndName = pSound->GetName();
		sndName.replace( ':','/' );

		IFacialEffectorsLibrary *pEffectorsLib = m_pModel->GetLibrary();

		IFacialAnimSequence* pSequence = NULL;
		bool exists = false;

		if (sndName.find('/') >= 0 || sndName.find('\\') >= 0)
		{
			const stack_string sequenceFileName = sndName;
			const stack_string fsqFile = PathUtil::ReplaceExtension(PathUtil::ToUnixPath(sequenceFileName),FACIAL_SEQUENCE_EXT);
			exists = gEnv->pCryPak->IsFileExist( fsqFile.c_str(),ICryPak::eFileLocation_Any );
			if (exists)
				pSequence = g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence( sndName.c_str() );
		}

		// Try finding the file in the same actual path as the sound file
		if (!exists)
		{
			sndName = pSound->GetFilePathAndName();
			if (sndName.find('/') >= 0 || sndName.find('\\') >= 0)
			{
				const stack_string sequenceFileName = sndName;
				const stack_string fsqFile = PathUtil::ReplaceExtension(PathUtil::ToUnixPath(sequenceFileName),FACIAL_SEQUENCE_EXT);
				exists = gEnv->pCryPak->IsFileExist( fsqFile.c_str(),ICryPak::eFileLocation_Any );
				if (exists)
					pSequence = g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence( sndName.c_str() );
			}
		}

		sndName = stack_string(PathUtil::GetFileName( sndName ));

		if (pEffectorsLib != NULL && (!exists))
		{
			// Try to find sequence in the same folder as facial expression.
			const stack_string sequenceFilename = PathUtil::Make( PathUtil::GetPath(stack_string(pEffectorsLib->GetName())),sndName );
			const stack_string fsqFile = PathUtil::ReplaceExtension(PathUtil::ToUnixPath(sequenceFilename),FACIAL_SEQUENCE_EXT);
			exists = gEnv->pCryPak->IsFileExist( fsqFile.c_str(),ICryPak::eFileLocation_Any );
			if (exists)
				pSequence = g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence( sequenceFilename.c_str() );
		}
		if (!exists)
		{
			// Try to find sequence in the same folder as character.
			const stack_string sequenceFilename = PathUtil::Make( PathUtil::GetPath(stack_string(m_pCharacter->GetFilePath())),sndName );
			const stack_string fsqFile = PathUtil::ReplaceExtension(PathUtil::ToUnixPath(sequenceFilename),FACIAL_SEQUENCE_EXT);
			exists = gEnv->pCryPak->IsFileExist( fsqFile,ICryPak::eFileLocation_Any );
			if (exists)
				pSequence = g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence( sequenceFilename );
		}

		if (bStop && exists)
		{
			if (pSequence)
				m_pAnimContext->StopSequence((CFacialAnimSequence*)(pSequence));
			m_waitingLipSync.Done();
			return;
		}

		// this also implies that the lip sync sequence is waiting
		m_waitingLipSync.StartWaiting(pSequence, nSoundId);
	}
}

void CFacialInstance::ProcessWaitingLipSync()
{
	if(m_waitingLipSync.IsWaiting())
	{
		IFacialAnimSequence* pSequence = m_waitingLipSync.GetAnimSequence();
		if (pSequence)
			if (pSequence->IsInMemory())
			{
			// We found a facial sequence corresponding to this file.
				ISound *pSound = gEnv->pSoundSystem->GetSound(m_waitingLipSync.GetSoundID());
				if (!pSound)
				{
					m_waitingLipSync.Done();
					return;
				}
				
				PlaySequence(pSequence, eFacialSequenceLayer_Dialogue, false);
				int nSoundMillis = pSound->GetInterfaceExtended()->GetCurrentSamplePos(true);
				SeekSequence( eFacialSequenceLayer_Dialogue,nSoundMillis/1000.0f );

				// not waiting anymore
				m_waitingLipSync.Done();
			}
	}
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::OnExpressionLibraryLoad()
{
	m_pEyeMovement->OnExpressionLibraryLoad();
}

//////////////////////////////////////////////////////////////////////////
IFacialAnimSequence* CFacialInstance::LoadSequence( const char *sSequenceName, bool addToCache )
{
	if (!sSequenceName)
		return 0;

	if (!m_pMasterInstance)
	{
		AnimWarning("Loading facial animation sequence failed - instance has no master character instance (Code error).");
		return 0;
	}

	IAnimationSet* pAnimationSet = (m_pMasterInstance ? m_pMasterInstance->GetIAnimationSet() : 0);
	if (!pAnimationSet)
	{
		AnimWarning("Loading facial animation sequence failed - facial animation instance has no animation set (Code error).");
		return 0;
	}

	const char* szSequencePattern = (pAnimationSet && sSequenceName ? pAnimationSet->GetFacialAnimationPathByName(sSequenceName) : 0);
	if (!szSequencePattern)
	{
		AnimWarning("Facial sequence \"%s\" not listed in CHRPARAMS file for character \"%s\".", sSequenceName, m_pMasterInstance->m_pModel->GetModelFilePath());
		return 0;
	}

	static const int MAX_PATH_LENGTH = 1024;
	char pathBuffer[MAX_PATH_LENGTH] = "";
	int patternLength = strlen(szSequencePattern);
	if (patternLength + 1 > MAX_PATH_LENGTH)
	{
		AnimWarning("Facial sequence CHRPARAMS file pattern for \"%s\" exceeds maximum length (%d)", sSequenceName, MAX_PATH_LENGTH);
		return 0;
	}

	if (szSequencePattern)
		PathExpansion::SelectRandomPathExpansion(szSequencePattern, pathBuffer);

	IFacialAnimSequence* pSequence = (szSequencePattern ? g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence(pathBuffer) : 0);
	return pSequence;
}

//////////////////////////////////////////////////////////////////////////
void PrecacheFacialExpressionCallback(void* userData, const char* expression)
{
	if (expression)
		g_pCharacterManager->GetIFacialAnimation()->StartStreamingSequence(expression);
}
void CFacialInstance::PrecacheFacialExpression(const char *sSequenceName)
{
	if (!sSequenceName)
		return;

	if (!m_pMasterInstance)
	{
		AnimWarning("Loading facial animation sequence failed - instance has no master character instance (Code error).");
		return;
	}

	IAnimationSet* pAnimationSet = (m_pMasterInstance ? m_pMasterInstance->GetIAnimationSet() : 0);
	if (!pAnimationSet)
	{
		AnimWarning("Loading facial animation sequence failed - facial animation instance has no animation set (Code error).");
		return;
	}

	const char* szSequencePattern = (pAnimationSet && sSequenceName ? pAnimationSet->GetFacialAnimationPathByName(sSequenceName) : 0);
	if (!szSequencePattern)
	{
		//GDC09HACK
	//	AnimWarning("Facial sequence \"%s\" not listed in CAL file for character \"%s\".", sSequenceName, m_pMasterInstance->m_pModel->GetModelFilePath());
		return;
	}

	const int MAX_PATH_LENGTH = 1024;
	int patternLength = strlen(szSequencePattern);
	if (patternLength + 1 > MAX_PATH_LENGTH)
	{
		//GDC09HACK
	//	AnimWarning("Facial sequence CAL file pattern for \"%s\" exceeds maximum length (%d)", sSequenceName, MAX_PATH_LENGTH);
		return;
	}

	if (szSequencePattern)
		PathExpansion::EnumeratePathExpansions(szSequencePattern, PrecacheFacialExpressionCallback, 0);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::UpdateProceduralFaceBehaviour()
{	 

	if (m_tProcFace.m_bEyesBlinking)
	{	
		if (!m_tProcFace.m_pBlink)
			m_tProcFace.m_pBlink = (CFacialEffector*)FindEffector( "Blink" );
		
		float fCurrTime=gEnv->pTimer->GetCurrTime();

		if (m_tProcFace.m_fBlinkingTime<0)
			m_tProcFace.m_fBlinkingTime=fCurrTime;

		if (m_tProcFace.m_pBlink)
		{			
			SFacialEffectorChannel effch;
			effch.status = SFacialEffectorChannel::STATUS_ONE;
			effch.bTemporary = true;
			effch.pEffector = m_tProcFace.m_pBlink;
			effch.fWeight = 1.0f; //Random(0.0f,1.0f);	 			
			m_pAnimContext->StartChannel( effch );
		}
		
		if (fCurrTime-m_tProcFace.m_fBlinkingTime>0.1f)
		{		
			m_tProcFace.m_bEyesBlinking=false;
			m_tProcFace.m_fBlinkingTime=-1;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
SPU_NO_INLINE void CFacialInstance::ApplyProceduralFaceBehaviour()
{

	if (m_tProcFace.m_bEnabled==false || Console::GetInst().ca_eyes_procedural==0)
	{
		m_tProcFace.m_bEyesBlinking=false;
		return;
	}

	float fCurrtime=gEnv->pTimer->GetCurrTime();	

	// simple rapid eyes movements, staring at eyes and mouth when talking
	if (fCurrtime-m_tProcFace.m_fLastJitterTime>m_tProcFace.m_fJitterTimeChange)
	{	
		m_tProcFace.m_fLastJitterTime=fCurrtime;

		// golden proportion 3/5 
		m_tProcFace.m_nCurrEyesJitter=(Random(13))&3; 		
		m_tProcFace.m_fJitterTimeChange=Random(0.1f,1.5f);	 	
		m_tProcFace.m_fEyesJitterScale=Random(0.002f,0.02f);  

		// eyes movements are in sync with eyes blinking.
		if ((Random(5)&3)==1)
			m_tProcFace.m_bEyesBlinking=true;
	}

	// moved to AdjustCamPosForProceduralFaceAnimation
	//vCamPos+=(m_tProcFace.m_JitterEyesPositions[m_tProcFace.m_nCurrEyesJitter]*m_tProcFace.m_fEyesJitterScale);

}

//////////////////////////////////////////////////////////////////////////
SPU_NO_INLINE void CFacialInstance::AdjustCamPosForProceduralFaceAnimation( Vec3 &vCamPos )
{
	vCamPos+=(m_tProcFace.m_JitterEyesPositions[m_tProcFace.m_nCurrEyesJitter]*m_tProcFace.m_fEyesJitterScale);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::EnableProceduralFacialAnimation( bool bEnable )
{
	m_tProcFace.m_bEnabled = bEnable;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::SetForcedRotations(int numForcedRotations, CFacialAnimForcedRotationEntry* forcedRotations)
{
	m_forcedRotations.resize(numForcedRotations);
	if (forcedRotations && numForcedRotations)
		memcpy(&m_forcedRotations[0], forcedRotations, numForcedRotations * sizeof(forcedRotations[0]));
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::SetMasterCharacter(ICharacterInstance* pMasterInstance)
{
	m_pMasterInstance = (CCharInstance*)pMasterInstance;
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::TemporarilyEnableBoneRotationSmoothing()
{
	if (Console::GetInst().ca_DebugFacial)
		CryLogAlways("CFacialInstance::TemporarilyEnableBoneRotationSmoothing - (this=%p) enabling bone rotation smoothing", this);
	m_pAnimContext->TemporarilyEnableAdditionalBoneRotationSmoothing();
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::Reset()
{
	if (Console::GetInst().ca_DebugFacial)
		CryLogAlways("CFacialInstance::Reset(this=%p)", this);

	IFacialAnimSequence* pOldSequence = (m_currentLayer >= 0 ? m_layers[m_currentLayer].sequence : _smart_ptr<IFacialAnimSequence>(0));
	for (int layer = 0; layer < eFacialSequenceLayer_COUNT; ++layer)
		m_layers[layer] = LayerInfo();
	m_currentLayer = -1;
	UpdateCurrentSequence(pOldSequence);
}

//////////////////////////////////////////////////////////////////////////
void CFacialInstance::UpdateCurrentSequence(IFacialAnimSequence* pPreviousSequence)
{
	m_currentLayer = -1;
	for ( int i = 0; i < eFacialSequenceLayer_COUNT; ++i )
	{
		if (m_layers[i].sequence)
		{
			m_currentLayer = i;
			LayerInfo& layer = m_layers[m_currentLayer];

			if (Console::GetInst().ca_DebugFacial)
				CryLogAlways("CFacialInstance::UpdateCurrentSequence (this=%p) - activating layer %d (sequence = \"%s\")", this, i, (layer.sequence ? layer.sequence->GetName() : "NULL"));

			break;
		}
	}

	// check if it changed
	LayerInfo newSequence;
	if (m_currentLayer >= 0)
		newSequence = m_layers[m_currentLayer];
	if (pPreviousSequence != newSequence.sequence)
	{
		// DEBUGGING
		//CryLogAlways("this = %p, prev = %p, new = %p", this, pPreviousSequence, newSequence.sequence);

		if (Console::GetInst().ca_DebugFacial)
			CryLogAlways("CFacialInstance::UpdateCurrentSequence - (this=%p) change detected", this);

		// stop old sequence
		if (pPreviousSequence)
		{
			if (Console::GetInst().ca_DebugFacial)
				CryLogAlways("CFacialInstance::UpdateCurrentSequence - (this=%p) stopping sequence \"%s\"", this, (pPreviousSequence ? pPreviousSequence->GetName() : "NULL"));
			m_pAnimContext->StopSequence((CFacialAnimSequence*)pPreviousSequence);
		}

		if (newSequence.sequence)
		{
			if (Console::GetInst().ca_DebugFacial)
				CryLogAlways("CFacialInstance::UpdateCurrentSequence - (this=%p) starting sequence \"%s\"", this, (newSequence.sequence ? newSequence.sequence->GetName() : "NULL"));
			m_pAnimContext->PlaySequence((CFacialAnimSequence*)newSequence.sequence.get(), newSequence.bExclusive, newSequence.bLooping);
		}

		if (Console::GetInst().ca_DebugFacial)
			CryLogAlways("CFacialInstance::UpdateCurrentSequence - (this=%p) enabling bone rotation smoothing", this);
		m_pAnimContext->TemporarilyEnableAdditionalBoneRotationSmoothing();
	}
}

void CFacialInstance::GetMemoryUsage( ICrySizer *pSizer ) const
{
	pSizer->AddObject(this, sizeof(*this));
	pSizer->AddObject( m_layers );
	pSizer->AddObject( m_currentLayer );
	pSizer->AddObject( m_pAnimContext );
	pSizer->AddObject( m_pEyeMovement );
	pSizer->AddObject( &m_forcedRotations, sizeof(m_forcedRotations) + sizeof(CFacialAnimForcedRotationEntry) * m_forcedRotations.capacity() );		
	pSizer->AddObject( m_waitingLipSync );
	pSizer->AddObject( m_tProcFace );
}

#include UNIQUE_VIRTUAL_WRAPPER(IFacialInstance)
